package feizhou.untils.tools.io;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtil {
    private static final Logger logger = LoggerFactory.getLogger(ZipUtil.class);
    private static final int BUFFER_SIZE = 2 * 1024;

    /**
     * 递归压缩文件
     *
     * @param source   源路径,可以是文件,也可以目录
     * @param destinct 目标路径,压缩文件名
     * @throws IOException
     */
    public static void compress(String source, String destinct)
            throws IOException {
        List fileList = loadFilename(new File(source));
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(destinct));

        byte[] buffere = new byte[8192];
        int length;
        BufferedInputStream bis;

        for (int i = 0; i < fileList.size(); i++) {
            File file = (File) fileList.get(i);
            zos.putNextEntry(new ZipEntry(getEntryName(source, file)));
            bis = new BufferedInputStream(new FileInputStream(file));

            while (true) {
                length = bis.read(buffere);
                if (length == -1) {
                    break;
                }
                zos.write(buffere, 0, length);
            }
            bis.close();
            zos.closeEntry();
        }
        zos.close();
    }

    /**
     * 递归获得该文件下所有文件名(不包括目录名)
     *
     * @param file
     * @return
     */
    private static List<File> loadFilename(File file) {
        List<File> fileList = new ArrayList();
        if (file.isFile()) {
            fileList.add(file);
        }
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                fileList.addAll(loadFilename(f));
            }
        }
        return fileList;
    }

    /**
     * 获得zip entry 字符串
     *
     * @param base
     * @param file
     * @return
     */
    private static String getEntryName(String base, File file) {
        File baseFile = new File(base);
        String filename = file.getPath();
        // int index=filename.lastIndexOf(baseFile.getName());
        if (baseFile.getParentFile().getParentFile() == null) {
            return filename.substring(baseFile.getParent().length());
        }
        return filename.substring(baseFile.getParent().length() + 1);
    }


    private static final int buffer = 2048;

    /**
     * 解压Zip文件
     *
     * @param zipFilePath zip 文件目录
     * @param destPath    解压的保存路径
     * @return void
     * @author <a href="920786312@qq.com">周飞</a>
     * @since 2023/11/28 15:57
     */
    public static void unZip(String zipFilePath, String destPath) {
        //如果保存路径不存在，就创建
        File destFile = new File(destPath);
        if(!destFile.exists()){
            destFile.mkdirs();
        }

        // 创建 ZipInputStream 对象
        try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath), StandardCharsets.UTF_8)) {
            // 逐个解压 ZipEntry
            ZipEntry entry = zipIn.getNextEntry();
            while (entry != null) {
                //文件保存路径
                String filePath = destPath + File.separator + entry.getName();
                // 如果 ZipEntry 是目录，则创建目录
                if (entry.isDirectory()) {
                    File dir = new File(filePath);
                    dir.mkdirs();
                } else {
                    // 如果 ZipEntry 是文件，则解压文件
                    unZip(zipIn, filePath);
                }
                zipIn.closeEntry();
                entry = zipIn.getNextEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解压zip
     * @param zipIn    zip文件流
     * @param filePath 保存路径
     * @return void
     * @author <a href="920786312@qq.com">周飞</a>
     * @since 2023/11/28 15:59
     */
    private static void unZip(ZipInputStream zipIn, String filePath) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        byte[] bytesIn = new byte[4096];
        int read;
        while ((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
    }

    /**
     * 压缩成ZIP
     *
     * @param srcFiles 需要压缩的文件列表
     * @param out      压缩文件输出流
     * @throws RuntimeException 压缩失败会抛出运行时异常
     */
    public static void toZip(File[] srcFiles, OutputStream out) {
        long start = System.currentTimeMillis();
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(out);
            for (File srcFile : srcFiles) {
                byte[] buf = new byte[8192];
                zos.putNextEntry(new ZipEntry(srcFile.getName()));
                int len;
                FileInputStream in = new FileInputStream(srcFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.closeEntry();
                in.close();
            }
            long end = System.currentTimeMillis();
            logger.debug("压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("系统异常：", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.error("系统异常：", e);
                }
            }
        }
    }

    /**
     * 获取一个文件的md5值(可处理大文件)
     *
     * @return md5 value
     */
    public static String getMD5(File file) {
        FileInputStream fileInputStream = null;
        try {
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fileInputStream.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }
            return new String(Hex.encodeHex(MD5.digest()));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String copyInputStreamToFileAndGetMd5Hex(InputStream inputStream, File file) throws IOException {

        MessageDigest digest = DigestUtils.getMd5Digest();

        FileOutputStream outputStream = null;

        try {
            outputStream = new FileOutputStream(file);
            byte[] buffer = new byte[2048];
            int read = inputStream.read(buffer);
            while (read > -1) {
                // 计算MD5,顺便写到文件
                digest.update(buffer, 0, read);
                outputStream.write(buffer, 0, read);

                read = inputStream.read(buffer);
            }
        } finally {
            IOUtils.closeQuietly(outputStream);
        }

        return Hex.encodeHexString(digest.digest());
    }

    /**
     * <p>
     * Title: ZipToBase64.java<／p>
     * <p>
     * Description: 压缩 转 base64 网络传输<／p>
     *
     * @param srcFiles 需要压缩的文件
     * @return Base64 压缩文件后进行base64编码
     * @version 1.0
     */
    public static String toBase64Zip(List<File> srcFiles) throws RuntimeException {
        logger.info("开始压缩文件    [{}]", srcFiles);
        long start = System.currentTimeMillis();
        String base64toZip = "";
        ZipOutputStream zos = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            zos = new ZipOutputStream(baos);
            for (File srcFile : srcFiles) {
                byte[] buf = new byte[BUFFER_SIZE];
                logger.info("压缩的文件名称    [{}]   ", srcFile.getName() + "压缩的文件大小      [{}] ", srcFile.length());
                zos.putNextEntry(new ZipEntry(srcFile.getName()));
                int len;
                FileInputStream in = new FileInputStream(srcFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.closeEntry();
                in.close();
            }
            long end = System.currentTimeMillis();
            logger.info("压缩完成，耗时：    [{}] ms", (end - start));
        } catch (Exception e) {
            throw new RuntimeException("zip error from ZipToBase64", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        byte[] refereeFileBase64Bytes = Base64.encodeBase64(baos.toByteArray());
        try {
            base64toZip = new String(refereeFileBase64Bytes, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("压缩流出现异常", e);
        }
        return base64toZip;
    }

    public static void main(String[] args) {
        //  compress("D:/李展召-2019年度工作总结.doc","d:/testZip4444.zip");
//        System.out.println(getMD5(new File("d:/b81ed2b289a52f4f6950d3170fcfe929.zip")));
        // unZip("D:/123.zip","D:/33");
        /* File file=new File("D:/李展召-2019年度工作总结.doc");
         File[] files=new File[1];
         files[0]=file;
        OutputStream outputStream=null;
        try {
             outputStream=new FileOutputStream(new File("D:/testQQ.zip"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        toZip(files,outputStream);*/

        String path = "C:\\Users\\26372\\Desktop\\temp\\1.zip";
        String path2 = "C:\\Users\\26372\\Desktop\\temp\\test\\";
        unZip(path, path2);


    }
}
